/*###################################################################
  > File Name: whitebox/src/test_redis.c
  > Author: Vurtune
  > Mail: vurtune@foxmail.com
  > Created Time: Tue 29 Aug 2017 01:52:49 AM PDT
###################################################################*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "dbg.h"
#include "list.h"
#include "redis_utils.h"

#define UNIT_CONUT (100000)

typedef struct {
        struct list_head hook;
        int reuse;
        char *key;
        void *value;
        size_t size;
        redisReply *reply;
} mset_seg_t;

static void __test_scan_set(void *key, void *_arg)
{
        (void) _arg;
        (void) key;
        printf("key %s\n", (char *)key);
}

static void __test_scan_hash(void *key, void *value, void *_arg)
{
        (void) _arg;
        (void) key;
        (void) value;
        printf("key %s, value %s\n", (char *)key, (char *)value);
}

int test_scan()
{
        int ret, port;
        redis_conn_t *conn;

        port = 6379;
        ret = redis_connect(&conn, &port, "/tmp/redis.sock");
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = redis_hiterator(conn, "testhash", __test_scan_hash, NULL);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = redis_siterator(conn, "testset", __test_scan_set, NULL);
        if (unlikely(ret))
                GOTO(err_ret, ret);
        
        return 0;
err_ret:
        return ret;
}

const int __test_hash(redis_conn_t *conn, const char *k, const char *v)
{
        int ret, buflen = MAX_BUF_LEN;
        char buf[MAX_BUF_LEN];
        
        ret = redis_hset(conn, "testhash", k, v, strlen(v) + 1);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        buflen = MAX_BUF_LEN;
        ret = redis_hget(conn, "testhash", k, buf, &buflen);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        YASSERT(strcmp(buf, v) == 0);
        printf("%s --> %s\n", k, v);

        return 0;
err_ret:
        return ret;
}

int test_hash()
{
        int ret, port, i;
        redis_conn_t *conn;
        char k[MAX_BUF_LEN], v[MAX_BUF_LEN];

        port = 6379;
        ret = redis_connect(&conn, &port, "/tmp/redis.sock");
        if (unlikely(ret))
                GOTO(err_ret, ret);

        for (i = 0;i < 1000; i++){
                snprintf(k, MAX_NAME_LEN, "key%u", i);
                snprintf(v, MAX_NAME_LEN, "value%u", i);
                
                __test_hash(conn, k, v);
        }
        
        return 0;
err_ret:
        return ret;
}

const int __test_set(redis_conn_t *conn, const char *k)
{
        int ret;//, buflen = MAX_BUF_LEN;
        //char buf[MAX_BUF_LEN];
        
        ret = redis_sset(conn, "testset", k);
        if (unlikely(ret))
                GOTO(err_ret, ret);

#if 0
        ret = redis_hget(conn, "testset", k, buf, &buflen);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        YASSERT(strcmp(buf, v) == 0);
        printf("%s --> %s\n", k, v);
#endif

        return 0;
err_ret:
        return ret;
}


int test_set()
{
        int ret, port, i;
        redis_conn_t *conn;
        char k[MAX_BUF_LEN];

        port = 6379;
        ret = redis_connect(&conn, &port, "/tmp/redis.sock");
        if (unlikely(ret))
                GOTO(err_ret, ret);

        for (i = 0;i < 1000; i++){
                snprintf(k, MAX_NAME_LEN, "set%u", i);
                
                __test_set(conn, k);
        }
        
        return 0;
err_ret:
        return ret;
}

int test_multi()
{
        int ret;
        redis_conn_t *conn;

        ret = redis_connect(&conn, NULL, "/tmp/redis.sock");
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = redis_multi(conn, "HMSET hash2 aa bb cc dd ee ff", "SADD set2 gg hh ii jj kk", NULL);
        if (unlikely(ret))
                GOTO(err_ret, ret);
        
        return 0;
err_ret:
        return ret;
}

int test_array()
{
        int ret;
        redis_conn_t *conn;
        char *array[100];

        ret = redis_connect(&conn, NULL, "/tmp/redis.sock");
        if (unlikely(ret))
                GOTO(err_ret, ret);

        array[0] = "HSET hash3 k1 v1";
        array[1] = "SADD set3 k1";
        array[2] = "HSET hash3 k2 v2";
        array[3] = "SADD set3 k2";
        array[4] = "HSET hash3 k3 v3";
        array[5] = "SADD set3 k3";
        array[6] = "HSET hash3 k4 v4";
        array[7] = "SADD set3 k4";
        array[8] = "HSET hash3 k5 v5";
        array[9] = "SADD set3 k5";

        ret = redis_exec_array(conn, (const char **)array, 10);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

int test_trans()
{
        int ret;
        redis_conn_t *conn;

        ret = redis_connect(&conn, NULL, "/tmp/redis.sock");
        if (unlikely(ret))
                GOTO(err_ret, ret);

        redis_trans_begin(conn);
        
        redis_trans_hset(conn, "hash4", "k1", "v1", 3);
        redis_trans_hset(conn, "hash4", "k2", "v2", 3);
        redis_trans_hset(conn, "hash4", "k3", "v3", 3);
        redis_trans_hset(conn, "hash4", "k4", "v4", 3);
        redis_trans_hset(conn, "hash4", "k5", "v5", 3);
        redis_trans_hset(conn, "hash4", "k6", "v6", 3);

        redis_trans_sset(conn, "set4", "k1");
        redis_trans_sset(conn, "set4", "k2");
        redis_trans_sset(conn, "set4", "k3");
        redis_trans_sset(conn, "set4", "k4");
        redis_trans_sset(conn, "set4", "k5");
        redis_trans_sset(conn, "set4", "k6");

        redis_trans_end(conn);
        
        return 0;
err_ret:
        return ret;
}

#define INSERT_COUNT 100000

int test_trans1()
{
        int ret;
        redis_conn_t *conn;
        char k[MAX_BUF_LEN], v[MAX_BUF_LEN];
        struct timeval t1, t2, t3, t4;

        ret = redis_connect(&conn, NULL, "/tmp/redis.sock");
        if (unlikely(ret))
                GOTO(err_ret, ret);

        _gettimeofday(&t1, NULL);
        
        redis_trans_begin(conn);

        _gettimeofday(&t2, NULL);
        
        for (int i = 0; i < INSERT_COUNT; i++) {
                snprintf(k, MAX_NAME_LEN, "k%u", i);
                snprintf(v, MAX_NAME_LEN, "v%u", i);
                redis_trans_hset(conn, "test_trans_create", k, v, strlen(v) + 1);
        }

        _gettimeofday(&t3, NULL);
        
        redis_trans_end(conn);

        _gettimeofday(&t4, NULL);

        DINFO("lock used %ju queue used %ju exec used %ju, insert count %u\n",
              _time_used(&t1, &t2),
              _time_used(&t2, &t3),
              _time_used(&t3, &t4),
              INSERT_COUNT);
              
        return 0;
err_ret:
        return ret;
}

int test_multi2()
{
        int ret;
        redis_conn_t *conn;
        char k[MAX_BUF_LEN], v[MAX_BUF_LEN];
        struct timeval t1, t2, t3;
        mctx_t mctx;

        ret = redis_connect(&conn, NULL, "/tmp/redis.sock");
        if (unlikely(ret))
                GOTO(err_ret, ret);

        redis_multi_init(&mctx);
        
        _gettimeofday(&t1, NULL);
        
        for (int i = 0; i < INSERT_COUNT; i++) {
                snprintf(k, MAX_NAME_LEN, "k%u", i);
                snprintf(v, MAX_NAME_LEN, "v%u", i);

                redis_multi_append(&mctx, k, v, strlen(v) + 1);
        }
        
        _gettimeofday(&t2, NULL);
        
        ret = redis_multi_exec(conn, "HMSET", "test_multi2", &mctx, NULL, NULL);
        if (unlikely(ret))
                GOTO(err_ret, ret);
        

        _gettimeofday(&t3, NULL);

        redis_multi_destory(&mctx);
        
        DINFO("queue used %ju exec used %ju, insert_count %u\n",
              _time_used(&t1, &t2),
              _time_used(&t2, &t3),
              INSERT_COUNT);
        
        return 0;
err_ret:
        return ret;
}

int test_multi3()
{
        int ret;
        redis_conn_t *conn;
        char k[MAX_BUF_LEN];
        struct timeval t1, t2, t3;
        mctx_t mctx;

        ret = redis_connect(&conn, NULL, "/tmp/redis.sock");
        if (unlikely(ret))
                GOTO(err_ret, ret);

        redis_multi_init(&mctx);
        
        _gettimeofday(&t1, NULL);
        
        for (int i = 0; i < INSERT_COUNT; i++) {
                snprintf(k, MAX_NAME_LEN, "k%u", i);

                redis_multi_append(&mctx, k, NULL, 0);
        }
        
        _gettimeofday(&t2, NULL);
        
        ret = redis_multi_exec(conn, "SADD", "test_multi3", &mctx, NULL, NULL);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        _gettimeofday(&t3, NULL);

        redis_multi_destory(&mctx);
        
        DINFO("queue used %ju exec used %ju, insert_count %u\n",
              _time_used(&t1, &t2),
              _time_used(&t2, &t3),
              INSERT_COUNT);
        
        return 0;
err_ret:
        return ret;
}


void redis_pipeline_begin(redis_conn_t *conn)
{
        int ret;
        
        ret = sy_rwlock_wrlock(&conn->rwlock);
        if ((unlikely(ret)))
                UNIMPLEMENTED(__DUMP__);
}

void redis_pipeline_append(redis_conn_t *conn, const char *str)
{
        int ret;

        ret = redisAppendCommand(conn->ctx, str);
        YASSERT(ret == 0);
}

void redis_pipeline_end(redis_conn_t *conn, int count)
{
        int ret;
        redisReply *reply;

        for (int i = 0; i < count; i++) {
                ret = redisGetReply(conn->ctx, (void **)&reply);
                DBUG("rep[%u] ret %u reply->type %u reply->str %s\n", i, ret, reply->type, reply->str);
                YASSERT(ret == 0);

        }
        
        sy_rwlock_unlock(&conn->rwlock);
}

int test_multi4()
{
        int ret;
        redis_conn_t *conn;
        char buf[MAX_BUF_LEN];
        struct timeval t1, t2, t3;

        ret = redis_connect(&conn, NULL, "/tmp/redis.sock");
        if (unlikely(ret))
                GOTO(err_ret, ret);

        redis_pipeline_begin(conn);
        
        _gettimeofday(&t1, NULL);
        
        for (int i = 0; i < INSERT_COUNT; i++) {
                snprintf(buf, MAX_NAME_LEN, "HSET test_multi4 k%u, v%u", i, i);

                redis_pipeline_append(conn, buf);
        }
        
        _gettimeofday(&t2, NULL);

        redis_pipeline_end(conn, INSERT_COUNT);
        
        _gettimeofday(&t3, NULL);

        DINFO("queue used %ju exec used %ju, insert_count %u\n",
              _time_used(&t1, &t2),
              _time_used(&t2, &t3),
              INSERT_COUNT);
        
        return 0;
err_ret:
        return ret;
}

int main(int argc, char *argv[])
{
        /*
        test_hash();
        test_set();
        test_scan();
        test_multi();
        test_array();
        test_trans_create();
        */

        test_trans1();
        test_multi2();
        test_multi3();
        test_multi4();
        
        return 0;
}
